<?php
/**
 * Created by Benjamin.
 * Date: 2020/4/30
 * Time: 10:52
 */
namespace App\WebSocket;


use App\Logic\UserLogic;
use App\Model\MsgModel;
use App\Model\UserModel;
use App\Services\BroadcastService;
use App\Services\GroupMsgService;
use App\Services\GroupService;
use App\Services\ChatService;
use App\Services\HelpService;
use App\Services\MsgService;
use App\Services\PrivateMsgService;
use App\Services\ReponseCodeService;
use App\Services\SystemMessageService;
use App\Services\UserGroupService;
use App\Services\UserService;
use EasySwoole\Component\TableManager;
use EasySwoole\EasySwoole\Config;
use EasySwoole\Socket\AbstractInterface\Controller;
use EasySwoole\Validate\Validate;



/**
 * Class Index
 *
 * 此类是默认的 websocket 消息解析后访问的 控制器
 *
 * @package App\WebSocket
 */
class Index extends Controller
{
    /**格式化响应
     * @param $messageArr
     */
    protected function formateReturn($messageArr){
        $param = $this->caller()->getArgs();
        $fdToAppTable = TableManager::getInstance()->get(Config::getInstance()->getConf('CHAT.fd_to_app'));
        $fdToApp =  $fdToAppTable->get($param['fd']);
        $this->response()->setMessage(HelpService::aesEncrypt(json_encode($messageArr,JSON_UNESCAPED_UNICODE),$fdToApp['access_key'],$fdToApp['secret_key']));
        return true;
    }


    /**
     * 客户端准备就绪
     * showdoc
     * @catalog Websocket
     * @title 客户端准备就绪
     * @description 客户端准备就绪，触发这个业务，服务端会主动下发群md5和群成员的md5,方便客户端对比数据变动
     * @method ws
     * @param action  是 string   动作，appready
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  group_hash string 全部群的哈希
     * @return_param  group_member_hash_list array 群列表
     * @return_param  group_member_hash_list.group_id string 群id
     * @return_param  group_member_hash_list.member_hash string  群成员哈希值
     * @remark  群哈希：全部群按升序顺序拼接group_id和version，然后md5，得到群整体的哈希；群成员哈希，每个群有一个，按升序，主要是判断群成员是否有变动，生成规则：拼接全部群成员app_user_id和version，然后把得到的字符串md5
     */
    public function appready(){
        $param = $this->caller()->getArgs();
       $broadcastService = new BroadcastService();
       $broadcastService->appready($param);
    }

    /**
     * 最近联系人
     * showdoc
     * @catalog Websocket
     * @title 最近联系人
     * @description 最近联系人
     * @method ws
     * @param action  是 string   动作，recent
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  friendList array 最近联系人
     * @return_param  groupList array 最近联系群
     * @return_param  serviceGroup array  客服群信息
     * @remark
     */
    public function recent(){
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $param = $this->caller()->getArgs();
        #计算下发未读消息
        go(function () use($param,$userInfo){
            $msgService = new MsgService();
            $msgService->recentChat($param,$userInfo);
        });
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_OK,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'recent',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
            'msg'=>ReponseCodeService::$phrases[ReponseCodeService::CODE_OK],
        );
        return  $this->formateReturn( $messageArr );
    }

    /**
     * 初始化结束
     * showdoc
     * @catalog Websocket
     * @title 初始化结束
     * @description 初始化结束
     * @method ws
     * @param action  是 string   动作，iniFinish
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark   客户端调用完iniFinish业务，才算正式登陆成功
     */
    public function iniFinish(){
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $param = $this->caller()->getArgs();
        #添加消息同步任务
        $syncTable = TableManager::getInstance()->get(Config::getInstance()->getConf('CHAT.sync_message'));
        $syncTable->set($userInfo['id'],['fd'=>$param['fd'],'user_id'=>$userInfo['id']]);
        #登录状态设置为正常
        $readyTable =  TableManager::getInstance()->get(Config::getInstance()->getConf('CHAT.user_ready'));
        $readyInfo['user_id'] = $userInfo['id'];
        $readyInfo['status'] = 1;
        $readyTable->set($userInfo['id'], $readyInfo);
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_OK,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'iniFinish',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
            'msg'=>ReponseCodeService::$phrases[ReponseCodeService::CODE_OK],
        );
        return  $this->formateReturn( $messageArr );
    }

    /**
     * 根据群号获取群信息
     * showdoc
     * @catalog Websocket
     * @title  根据群号获取群信息
     * @description 根据群号获取群信息
     * @method ws
     * @param extra.group_id 是 string 群id，必须传
     * @param action  是 string   动作，getGroupDetail
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  group_name string 群名
     * @return_param  cover string 群封面
     * @return_param  id string 群id
     * @return_param  role string 我在群成员在群的角色1成员2管理员3创建人
     * @return_param  member_count int 群总人数
     * @return_param  created_at string 群创建时间
     * @return_param  updated_at string 群更新时间
     * @return_param  top int 是否制定：1置顶，0不置顶
     * @return_param  nickname_in_group string 本人在群里面的昵称，如果没有设置过会有个空值
     * @return_param  group_rename string 本人对这个群设置的别名，如果没有设置过会有个空值
     * @return_param  msg_setting int 1接收消息并提醒2接收消息不提醒3收进群助手且不提醒4屏蔽群消息5屏蔽群内图片
     * @return_param  notice string 群公告
     * @return_param  invite_mode int 是否禁言：1禁言，0不禁言
     * @return_param  nickname_in_group string 自己在群的昵称
     * @remark  2020-08-26,增加置顶top参数，2020-08-31增加nickname_in_group、group_rename
     */
    public function getGroupDetail(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group id')->required('group_id is require');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'getGroupDetail',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn( $messageArr);
        }

        $groupService = new GroupService();
        $ret = $groupService->groupDetail($param,$userInfo);

        if(!is_numeric($ret)){
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_OK,
                'data'=>json_encode($ret,JSON_FORCE_OBJECT),
                'action'=>'getGroupDetail',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
                'msg'=>ReponseCodeService::$phrases[$ret],
            );
        }else{
            $messageArr = array(
                'code'=>$ret,
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'getGroupDetail',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
                'msg'=>ReponseCodeService::$phrases[$ret],
            );
        }
        return  $this->formateReturn( $messageArr );
    }

    /**
     * 修改群信息
     * showdoc
     * @catalog Websocket
     * @title  修改群信息
     * @description 修改群信息
     * @method ws
     * @param extra.group_id 是 string 群id，必须传
     * @param extra.cover 否 string 群封面
     * @param extra.group_name 否 string 群名字
     * @param extra.banned_to_post 否 int 是否禁言：1禁言，0不禁言，开启后除了群主都不能发言
     * @param extra.invite_mode  否 int 是否开启邀请模式：1开启，0不开启
     * @param extra.notice 否 string 群公告，暂定限制300字
     * @param extra.app_user_id 否 string  新的群主app_user_id,注意，这个人必须已经是群成员，不是群成员会变更失败
     * @param action  是 string   动作，editGroup
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 修改群信息，目前只有管理员或者创建人才可编辑,完成编辑会发送一条系统消息做同步。注意，这边的群封面是属于sdk的信息，所以图片上传请走sdk，cover也请给文件哈希即可
     */
    public function editGroup(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group id')->required('group_id is require')->numeric('group_id 是数字格式');
        if(isset($param['extra']['invite_mode'])){
            $valitor->addColumn('invite_mode', 'invite_mode')->inArray([0,1],'invite_mode error');
        }

        if(isset($param['extra']['banned_to_post'])){
            $valitor->addColumn('banned_to_post', 'banned_to_post')->inArray([0,1],'banned_to_post error');
        }

        if(isset($param['extra']['invite_mode'])){
            $valitor->addColumn('invite_mode', 'invite_mode')->inArray([0,1],'invite_mode error');
        }

        if(isset($param['extra']['notice'])){
            $valitor->addColumn('notice', 'notice')->lengthMax(300,'公告超过长度');
        }

        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'editGroup',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn( $messageArr);
        }

        $groupService = new GroupService();
        $ret = $groupService->editGroup($param,$userInfo);
        if($ret['code'] ==ReponseCodeService::CODE_OK){
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_OK,
                'action'=>'editGroup',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
            $data['extra']['msg_id']  = $ret['msg_id'];
            $messageArr['data'] = json_encode($data);
        }else{
            $messageArr = array(
                'code'=>$ret['code'],
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'editGroup',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
                'msg'=>ReponseCodeService::$phrases[$ret['code']]
            );
        }
        return  $this->formateReturn( $messageArr );
    }

    /**
     * 主动推送系统消息（根据msg_id）
     * showdoc
     * @catalog Websocket
     * @title  主动推送系统消息（根据msg_id）
     * @description   根据msg_id，主动推送系统消息
     * @method ws
     * @param action  是  string  动作，pushMessage
     * @param extra.msg_id 是 string 消息id，如果客户端请求，则服务端会主动推送该消息到客户端，这个服务只对系统消息有效
     * @return {"code":200,"result":'',"msg":"sucess"}
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 2020-09-08
     */
    public function pushMessage(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('msg_id', 'msg_id')->required('msg_id必须传')->notEmpty('msg_id必须传');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'pushMessage',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $systemMessageService = new SystemMessageService();
        $ret = $systemMessageService->sendSysmessage($param,$userInfo);
        $messageArr['code'] = $ret;
        $messageArr['msg'] = ReponseCodeService::$phrases[$ret];
        return  $this->formateReturn( $messageArr);
    }

    /**
     * 创建群
     * showdoc
     * @catalog Websocket
     * @title  创建群
     * @description 创建群的接口
     * @method ws
     * @param extra.app_user_ids 是 string 群成员id，至少选中2个人，id用,分隔，如：1,2
     * @param extra.group_name 否 string 群名字
     * @param extra.cover 否 string 群封面图
     * @param action  是 string   动作，createGroup
     * @return {"code":200,"msg":"sucess","data":"{\"extra\":{\"group_info\":{\"id\":76,\"app_id\":2,\"app_user_id\":\"9a4297c33d9328934e496a4074863cfs\",\"user_id\":8,\"group_name\":\"1594299274\",\"service_user_id\":0,\"cover\":\"https:\\\/\\\/p.qqan.com\\\/up\\\/2020-6\\\/2020060308522797777.jpg\",\"notice\":\"\\u2018\\u2019\",\"status\":1,\"member_count\":3,\"banned_to_post\":0,\"created_at\":\"2020-07-09 20:54:34.000000\",\"updated_at\":\"2020-07-09 20:54:34.000000\"},\"member_list\":[{\"user_id\":8,\"group_id\":76,\"app_user_id\":\"572402b0988e33369ad8ff0d3cd440a9\",\"role\":3,\"nickname\":\"\\u5c0f\\u767d\\u6328\\u997f\\u7684\\u65f6\\u5019\",\"head\":\"http:\\\/\\\/img.xmfish.com\\\/attachment\\\/upload\\\/middle\\\/67\\\/2233367.jpg\"},{\"user_id\":31,\"group_id\":76,\"app_user_id\":\"572f02b0988e3x369ad8ff0d3cd440a9\",\"role\":1,\"nickname\":\"\",\"head\":\"\"},{\"user_id\":13,\"group_id\":76,\"app_user_id\":\"9a4297c33d9328934e496a4074863cfs\",\"role\":1,\"nickname\":\"JC02\",\"head\":\"http:\\\/\\\/img.xmfish.com\\\/attachment\\\/upload\\\/middle\\\/08\\\/1177508.gif\"}]}}","action":"createGroup","nonce":1594299273,"time":1594299274742,"user_id":8}
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  extra.group_info.id string 群的id
     * @return_param  extra.group_info.member_count int 群的成员数
     * @return_param  extra.group_info.app_user_id string 群创建人的app_user_id
     * @return_param  extra.group_info.user_id string 群的创建人用户id
     * @return_param  extra.group_info.group_name string 群名
     * @return_param  extra.group_info.service_user_id string 客服用户id，做客服功能会用到
     * @return_param  extra.user_groups.user_id string 群成员用户id
     * @return_param  extra.user_groups.group_id string 群id
     * @return_param  extra.user_groups.nickname string 群成员昵称
     * @return_param  extra.user_groups.head string 群成员头像
     * @return_param  extra.user_groups.role string 群成员在这群的角色
     * @return_param  nickname_in_group string 本人在群里面的昵称，如果没有设置过会有个空值
     * @return_param  group_rename string 本人对这个群设置的别名，如果没有设置过会有个空值
     * @return_param  extra.msg_id string 通知消息的msg_id
     * @remark 2020-09-07:增加返回系统消息的msg_id，可以端可以拿着这个msg_id发起消息通知
     */
    public function createGroup(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('app_user_ids', '用户id')->required('用户id必须传');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'createGroup',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $appUserIdArr = explode(',',$extra['app_user_ids']);
        if(count( $appUserIdArr )<2){
            unset( $appUserIdArr );
            return  $this->formateReturn($messageArr);
        }
        $groupService = new GroupService();
        return  $this->formateReturn( $groupService->createGroup($param,$userInfo));
    }


    /**
     * 群成员列表
     * showdoc
     * @catalog Websocket
     * @title  群成员列表
     * @description   群成员列表
     * @method ws
     * @param action  是  string  动作，groupMemberList
     * @param extra.group_id 是 string 群id
     * @param extra.id 否 int 最后一条插入的群成员id，如果传了，这个id之后的新成员会被下拉
     * @param extra.page 是 int  页码，
     * @param extra.limit 否 int  不传会得到全部数据
     * @return {"code":200,"result":'',"msg":"sucess"}
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  extra.nickname string 群成员昵称
     * @return_param  extra.head string 群成员头像
     * @return_param  extra.role string 群成员在群的角色
     * @return_param  extra.app_user_id string 群成员的app_user_id
     * @return_param  nickname_in_group string 成员在群里面的昵称，如果没有设置过会有个空值
     * @return_param extra.page int  当前页码
     * @return_param extra.limit  int  当前页每页条数
     * @return_param total int 总数据条数
     * @return_param total_page  int 总分页数
     * @return_param extra.status  int 状态：1正常，0退出状态
     * @return_param  updated_at int 最后更新时间
     * @return_param created_at int 创建时间
     * @remark 2020-09-02增加分页,客户端直接拉取下一页，直到没数据即可，服务端不再下发统计多少页、多少条
     */
    public function groupMemberList(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('group_id', '群group_id')->required('群group_id必须传')->numeric('群id必须是数字')->notEmpty('群id不为空');
        $valitor->addColumn('page', 'page')->required('page必须传')->numeric('page必须是数字')->notEmpty('page不为空');

        if(isset($param['extra']['updated_at'])){
            $valitor->addColumn('updated_at', 'updated_at')->numeric('updated_at必须是数字')->notEmpty('updated_at不为空');
        }
        if(isset($param['extra']['limit'])){
            $valitor->addColumn('limit', 'limit')->numeric('limit必须是数字')->notEmpty('limit不为空');
        }
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'groupMemberList',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $groupService = new GroupService();
        return  $this->formateReturn($groupService->groupMemberList($param));
    }

    /**
     * showdoc
     * @catalog Websocket
     * @title  登录
     * @description 登录
     * @method websocket
     * @url /
     * @param content string  是 发送的内容
     * @param action  string  是 动作，固定是login
     * @param  nonce string 是 消息随机字符串，由时间戳+6位随机字符串，用于消息校验
     * @param  extra string 是 额外信息，有需要增加的字段，全部往这边塞
     * @param  extra.token string 否 如果app有存token，请上传token，如果没有，可不传，服务端会自动判断，有传token,当不一致会提示客户端重新登录
     * @param  extra.access_key  string  是 开放平台的ak
     * @param  extra.app_user_id  string 是 用户唯一标识，请不要设为空、 null ，或 "null"
     * @param  extra.nickname  string 否 用户昵称
     * @param  extra.head  string 否 头像，必须传完整地址
     * @param  extra.type  string 否 用户类型1普通用户2客服3管理员,不传默认普通用户
     * @return {"code":200,"data":"{\"extra\":{\"id\":29,\"app_id\":2,\"app_user_id\":\"jianjianxianshen1g\",\"nickname\":\"\\u5c0f\\u767d\\u6328\\u997f\\u7684\\u65f6\\u5019\",\"head\":\"http:\\\/\\\/img.xmfish.com\\\/attachment\\\/upload\\\/middle\\\/67\\\/2233367.jpg\",\"type\":1,\"status\":1,\"created_at\":\"2020-07-02 15:27:05.000000\",\"updated_at\":\"2020-07-02 15:27:05.000000\"}}","action":"login","nonce":1594204722,"time":1594204722656,"msg":"sucess"}
     * @return_param  code int 状态码(200:成功0:失败)
     * @return_param  data string 返回结果
     * @return_param  msg string 返回提示语
     * @return_param  nonce string 消息随机字符串，由时间戳加6位随机字符串，用于消息校验
     * @return_param nickname 昵称
     * @return_param head 头像
     * @return_param id 用户id
     * @return_param token 用户登录token,如果客户本地有存储，登录时候请一起带上，服务端会校验token是否刷新过
     * @remark 没有登录过的用户会自动注册，2020-08-27
     */
    public function login(){
        $param = $this->caller()->getArgs();
        $valitor = new Validate();
        $valitor->addColumn('extra', '额外信息')->required('额外信息必须传');
        $valitor->addColumn('nonce', '随机字符串')->required('随机字符串必须传');
        $bool = $valitor->validate($param);
        $data = array(
            'extra'=>isset($param['extra'])?$param['extra']:array(1)
        );
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode($data,JSON_FORCE_OBJECT),
            'action'=>'login',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
        );
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            $messageArr['code'] = ReponseCodeService::CODE_BAD_REQUEST;
            return  $this->formateReturn($messageArr);
        }
        go(function ()use($param){
             $userService = new UserService();
             $userService->userLogin($param);
        });
    }

    /**
     * 群成员管理（设置管理员等）
     * showdoc
     * @catalog Websocket
     * @title   群成员管理（设置管理员等）
     * @description  群成员管理（设置管理员等）,只有群主可以设置管理员
     * @method ws
     * @param action  是  string  动作，groupMemberSetting
     * @param extra.group_id 是 int  群id
     * @param extra.role 是 int  群角色：1成员，2管理员
     * @param extra.app_user_ids 是 string 新管理员app_user_id,多个管理员用","分割，例如：geO0q5f3f89bd43c96或者geO0q5f3f89bd43c96,O0MWV5f3f8a2bae7fa
     * @return
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark  2020-08-26
     */
    public function groupMemberSetting(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];

        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group_id')->required('group_id必须传')->notEmpty('group_id不能为空');
        $valitor->addColumn('role', 'role')->required('role必须传')->notEmpty('role不能为空')->inArray([1,2],true,'role参数错误');
        $valitor->addColumn('app_user_ids', 'app_user_ids')->required('app_user_ids必须传')->notEmpty('app_user_ids不能为空');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'groupMemberSetting',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }
        $groupService = new GroupService();
        $ret = $groupService->groupMemberSetting($extra,$userInfo);
        $messageArr['code'] = $ret['code'];
        $messageArr['msg'] = ReponseCodeService::$phrases[$ret['code']];
        return  $this->formateReturn($messageArr);
    }

    /**
     * 自定义系统消息
     * showdoc
     * @catalog Websocket
     * @title  自定义系统消息
     * @description 自定义系统消息，用来对接各种自定义系统消息
     * @method ws
     * @param action  是  string  动作，diySysMessage
     * @param extra.sub_type 是 string 消息类型：group为群的系统消息，personal为对个人的系统消息
     * @param extra.associated_id 是 string 关联id，如果是发给群，请写群id，如果是发给个人，请写对方app_user_id
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 自定义系统消息，除了sub_type和associated_id会做校验，其他信息客户端随意填写，服务端会原原本本下发
     */
   public function diySysMessage(){
       $param = $this->caller()->getArgs();
       $userInfo =  $this->caller()->getArgs()['userInfo'];

       $extra =  isset($param['extra'])?$param['extra']:array(1);
       $valitor = new Validate();
       $valitor->addColumn('sub_type', 'sub_type')->required('sub_type必须传')->notEmpty('sub_type不能为空')->inArray(['group','personal'],true,'参数错误');
       $valitor->addColumn('associated_id', 'associated_id')->required('associated_id必须传')->notEmpty('associated_id不能为空');
       $messageArr = array(
           'code'=>ReponseCodeService::CODE_BAD_REQUEST,
           'data'=>json_encode(array(),JSON_FORCE_OBJECT),
           'action'=>'diySysMessage',
           'nonce'=>$param['nonce'],
           'time'=>HelpService::msectime(),
           'user_id'=>$userInfo['id'],
       );
       $bool = $valitor->validate($extra);
       if (!$bool) {
           $messageArr['msg'] = $valitor->getError()->__toString();
           return  $this->formateReturn(  $messageArr);
       }
       $sysMessageService = new SystemMessageService();
       switch ($extra['sub_type']){
           case 'group';
            $groupSysMessage= array(
                'group_id'=> $extra['associated_id'],
                'content'=>isset($param['content'])?$param['content']:'',
                'type'=>MsgModel::MSG_TYPE_DIY,
                'extra'=>$extra,
                'nonce'=> $param['nonce'],
                'app_id'=>$userInfo['app_id']
               );
         #   $ret = $sysMessageService->groupSysMessag($groupSysMessage);
              break;

           case 'personal':
               $SysMessage= array(
                   'to_app_user_id'=> $extra['associated_id'],
                   'content'=>isset($param['content'])?$param['content']:'',
                   'type'=>MsgModel::MSG_TYPE_DIY,
                   'extra'=>$extra,
                   'nonce'=> $param['nonce'],
               );
             $ret = $sysMessageService->privateSysMsg($SysMessage,$userInfo);
               break;
           default:
               $messageArr['code'] = ReponseCodeService::CODE_BAD_REQUEST;
               $messageArr['msg'] = ReponseCodeService::$phrases[ ReponseCodeService::CODE_BAD_REQUEST];
               $ret = $messageArr;
       }


       return  $this->formateReturn(  $ret);
   }

    /**
     * 清理消息
     * showdoc
     * @catalog Websocket
     * @title  清理消息
     * @description 清理消息
     * @method ws
     * @param action  是  string  动作，clearMsg
     * @param extra.msg_type 是 int 消息类型：1单聊，2群聊
     * @param extra.id 是 string 单聊时候请传用户app_user_id，群聊传群id
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 清理消息
     */
    public function clearMsg(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];

        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('msg_type', 'msg_type')->required('msg_type必须传')->notEmpty('msg_type不能为空');
        $valitor->addColumn('id', 'id')->required('id必须传')->notEmpty('id不能为空');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'clearMsg',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

         if($extra['msg_type']==1){
            $prvateMsgService = new PrivateMsgService();
            $ret = $prvateMsgService->clearMsg($param,$userInfo);
         }else{
             $groupMsgService = new GroupMsgService();
             $ret = $groupMsgService->clearMsg($param,$userInfo);
         }
         $messageArr['code'] = $ret;
         $messageArr['msg'] = ReponseCodeService::$phrases[$ret];
        return  $this->formateReturn($messageArr);
    }

    /**
     * 根据app_user_id获取用户信息
     * showdoc
     * @catalog Websocket
     * @title  根据app_user_id获取用户信息
     * @description 根据app_user_id获取用户信息，主要用来刷新用户头像、昵称
     * @method ws
     * @param action  是  string  动作，getOtherUserInfo
     * @param extra.app_user_id 是 string 用户app_user_id
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  nickname string 用户昵称
     * @return_param  head string 头像地址
     * @remark 用来刷新用户信息
     */
    public function getOtherUserInfo(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $userService = new UserService();
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'getOtherUserInfo',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $ret = $userService->getOtherUserInfo($param,$userInfo);
        if(is_object( $ret)){
            $data['extra'] =  $ret;
            $messageArr['data'] = json_encode($data);
            $messageArr['code'] = ReponseCodeService::CODE_OK;
        }else{
            $messageArr['code'] = $ret;
            $messageArr['code'] = ReponseCodeService::$phrases[$ret];
        }

        return  $this->formateReturn($messageArr);
    }


    /**
     * 更新用户信息
     * showdoc
     * @catalog Websocket
     * @title  更新用户信息
     * @description 更新用户信息
     * @method ws
     * @param action  是  string  动作，updateUserInfo
     * @param extra.nickname 否 string 用户昵称
     * @param extra.head 否 string 用户头像
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 修改用户信息，系统会自动同步给有联系过的人以及群成员
     */
    public function updateUserInfo(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];

        $userService = new UserService();
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'updateUserInfo',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $ret = $userService->updateUserInfo($param,$userInfo);
        $messageArr['code'] = $ret;
        return  $this->formateReturn($messageArr);
    }

    /**
     * 最近联系的用户顾客
     * showdoc
     * @catalog Websocket
     * @title 最近联系的用户顾客
     * @description 最近联系的用户顾客，用于客服登录后拉取最近联系的顾客
     * @method ws
     * @param action  是  string  动作，recentUsers
     * @param extra.page 是 int 页码
     * @param extra.count 是 string 每页多少条数据
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 用于客服登录后拉取最近联系的顾客
     */
    public function recentUsers(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group id')->required('group_id必须传');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'recentUsers',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $groupMsgService = new GroupMsgService();
        $list = $groupMsgService->list($param, $userInfo);
        if(is_array($list)){
            $data['extra']['list'] =  $list;
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_OK,
                'data'=>json_encode($data),
                'action'=>'recentUsers',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
        }else{
            $messageArr = array(
                'code'=>$list,
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'recentUsers',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
        }
        return  $this->formateReturn($messageArr);
    }

    /**
     * 同步最近一条消息
     * showdoc
     * @catalog Websocket
     * @title  批量同步最近一条消息
     * @description 批量同步最近一条消息
     * @method ws
     * @param action  是  string  动作，syncLastMsg
     * @param extra.condition 是 string 群id和用户app_user_id的字符串，字符串里面的type,1表示单聊，2表示群聊，样本,[{"type":1,"id":88},{"type":1,"id":22},{"type":2,"id":33},{"type":2,"id":44}]
     * @return
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  list 结果列表
     * @return_param type 类型：1单聊，2群聊
     * @return_param last_message 最后一条消息
     * @remark
     */
    public function syncLastMsg(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $chatService = new ChatService();
        $messageArr = array(
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'syncLastMsg',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
        );
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('condition', 'condition')->required('condition is require');
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['code']=ReponseCodeService::CODE_BAD_REQUEST;
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $list = $chatService->syncLastMsg($param,$userInfo);
        if(!is_array($list)){
            $messageArr['code']= $list;
            $messageArr['msg'] = ReponseCodeService::$phrases[$list];
            return  $this->formateReturn(  $messageArr);
        }
        $messageArr['extra']['list'] = $list;
        $messageArr['code']= ReponseCodeService::CODE_OK;
        $messageArr['msg'] = ReponseCodeService::$phrases[ReponseCodeService::CODE_OK];
        return  $this->formateReturn(  $messageArr);
    }

    /**
     * 我的群
     * showdoc
     * @catalog Websocket
     * @title  我的群
     * @description 我的群
     * @method ws
     * @param action  是  string  动作，myGroup
     * @param extra.type 是 string 群类型，0全部群，1普通群，2我管理的群，3我创建的群
     * @param extra.id 否 int 启始群id
     * @param extra.page 是 int  页码，
     * @param extra.limit 否 int  每页数据条数，不传会得到全部数据
     * @return
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  group_name string 群名
     * @return_param  cover string 群封面
     * @return_param  id string 群id
     * @return_param  role string 我在群成员在群的角色1成员2管理员3创建人
     * @return_param  member_count int 群总人数
     * @return_param  created_at string 群创建时间
     * @return_param  updated_at string 群更新时间
     * @return_param  top int 是否制定：1置顶，0不置顶
     * @return_param  nickname_in_group string 本人在群里面的昵称，如果没有设置过会有个空值
     * @return_param  group_rename string 本人对这个群设置的别名，如果没有设置过会有个空值
     * @return_param  msg_setting int 1接收消息并提醒2接收消息不提醒3收进群助手且不提醒4屏蔽群消息5屏蔽群内图片
     * @return_param  notice string 群公告
     * @return_param  invite_mode int 是否禁言：1禁言，0不禁言
     * @return_param  banned_to_post int 是否禁言：1禁言，0不禁言
     * @return_param  nickname_in_group string 自己在群的昵称
     * @return_param extra.page int  当前页码
     * @return_param extra.limit  int  当前页每页条数
     * @return_param total int 总数据条数
     * @return_param total_page  int 总分页数
     * @return_param extra.status  int 状态：1正常，0退出状态
     * @return_param  updated_at int 最后更新时间
     * @return_param created_at int 创建时间
     * @return_param  version int 版本号
     * @remark  2020-08-26,增加置顶top参数，2020-08-31增加nickname_in_group、group_rename，2020-09-16增加版本号version
     */
    public function myGroup(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('type', 'type')->required('type必须传')->inArray([0,1,2,3],'type格式错误')->notEmpty('type不为空');
        $valitor->addColumn('page', 'page')->required('page必须传')->numeric('page必须是数字')->notEmpty('page不为空');

        if(isset($param['extra']['updated_at'])&&!empty($param['extra']['updated_at'])){
            $valitor->addColumn('updated_at', 'updated_at')->numeric('updated_at必须是数字')->notEmpty('updated_at不为空');
        }

        if(isset($param['extra']['limit'])){
            $valitor->addColumn('limit', 'limit')->required('limit必须传')->numeric('limit必须是数字')->notEmpty('limit不为空');
        }

        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'myGroup',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }
        $groupService = new GroupService();
        return  $this->formateReturn($groupService->myGroup($param));
    }



    /**
     * 群设置（非群主）
     * showdoc
     * @catalog Websocket
     * @title  群设置
     * @description 消息是否屏蔽、自定义群名字、昵称等
     * @method ws
     * @param action  是  string  动作，groupSetting
     * @param extra.group_id 是 int 群id
     * @param extra.msg_setting 否 int 群设置：1接收消息并提醒2接收消息不提醒3收进群助手且不提醒4屏蔽群消息5屏蔽群内图片
     * @param extra.top 否 int 是否制定：1置顶，0不置顶
     * @param extra.nickname_in_group 否 string 群成员在群里的昵称，不超过20字
     * @param extra.group_rename 否 string 群别名，不超过20字，这名字相当于对群的备注，群如果有备注请限制备注的名字
     * @return
     * @return_param  code int 状态码(200:成功0:失败)
     * @remark 2020-08-31，增加nickname_in_group，group_rename
     */
    public function groupSetting(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $userGroupService = new UserGroupService();
        $messageArr = array(
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'groupSetting',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
        );
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group id')->required('group_id is require');
        if(isset($param['extra']['msg_setting'])){
            $valitor->addColumn('msg_setting', 'msg_setting')->required('msg_setting is require')->between(0,6)->notEmpty();
        }
        if(isset($param['extra']['top'])){
            $valitor->addColumn('top', 'top')->required('msg_setting is require')->inArray([0,1])->notEmpty();
        }
        if(isset($param['extra']['nickname_in_group'])){
            $valitor->addColumn('nickname_in_group', 'nickname_in_group')->lengthMax(20,'nickname_in_group最长20字符');
        }
        if(isset($param['extra']['group_rename'])){
            $valitor->addColumn('group_rename', 'group_rename')->lengthMax(20,'group_rename最长20字符');
        }

        $bool = $valitor->validate($extra);
        if (!$bool||count($param['extra'])<=0) {
            $messageArr['code']=ReponseCodeService::CODE_BAD_REQUEST;
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $ret = $userGroupService->groupSetting($param,$userInfo);
        $messageArr['code']=$ret;
        $messageArr['msg'] = ReponseCodeService::$phrases[$ret];
        return  $this->formateReturn(  $messageArr);
    }


    /**
     * 黑名单列表
     * showdoc
     * @catalog Websocket
     * @title  黑名单列表
     * @description 获取黑名单列表
     * @method ws
     * @param action  是  string  动作，getBlacklist
     * @return
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  id  string 用户id
     * @return_param  app_user_id  string 用户app_user_id
     * @return_param  nickname  string 用户昵称
     * @return_param  head string 用户头像
     * @remark 获取黑名单列表
     */
    public function getBlacklist(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];

        $userService = new UserService();
        $ret = $userService->getBlacklist($userInfo);

        if(!is_numeric( $ret)){
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_OK,
                'data'=>json_encode(array('extra'=>$ret),JSON_FORCE_OBJECT),
                'action'=>'getBlacklist',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'msg'=>ReponseCodeService::$phrases[ReponseCodeService::CODE_OK]
            );
        }else{
            $messageArr = array(
                'code'=>$ret,
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'getBlacklist',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'msg'=>ReponseCodeService::$phrases[$ret]
            );
        }

        return  $this->formateReturn( $messageArr);
    }

    /**
     * 用户信息不全，主动断开
     * showdoc
     * @catalog Websocket
     * @title 登录异常
     * @description  登录异常，用户需要重新登录才能继续正常使用
     * @return_param  code int 状态码(200:成功0:失败816:非客服无法操作)
     * @return_param  type int  类型20，服务端主动断开
     * @return_param sub_type string  personal
     * @remark 这种情况，是服务端存储的用户在线信息不全，正常是因为被挤掉登录信息。比如，张三用A手机登录，然后又用B手机登录，A手机的登录信息会被直接替换。
     */
    public function autoOffline(){
        $messageArr['msg'] = ReponseCodeService::$phrases[ReponseCodeService::CODE_UNAUTHORIZED];
        $sysMessageArr = array(
            'content'=>'',
            'action'=>'autoOffline',
            'type'=>PrivateMsgService::MSG_TYPE_AUTO_BREAK,
            'extra'=>json_encode(array(),JSON_FORCE_OBJECT),
            'sub_type'=>PrivateMsgService::MSG_SUB_TYPE_PERSONAL,
            'nonce'=>HelpService::msectime().HelpService::createNonce(6),
            'msg_id'=>HelpService::msectime().HelpService::createNonce(6),
            'time'=>HelpService::msectime(),
            'code'=>ReponseCodeService::CODE_LOGIN_INFO_NO_FUND,
        );
        return  $this->formateReturn($sysMessageArr);
    }
    /**
     * 切换客服
     * showdoc
     * @catalog Websocket
     * @title  切换客服
     * @description  切换客服，用户会自动连接新的客服
     * @method ws
     * @param extra.group_id 是 string 客服群id，必须传
     * @param extra.app_user_id 是 string 客服的app_user_id
     * @param action  是 string   动作，switchService
     * @return_param  code int 状态码(200:成功0:失败816:非客服无法操作)
     * @remark 当需要其他客服来处理事情，可以把新的客户app_user_id转接，转接后跟用户对话的会是新的客服。注意，只有管理员、客服才可以切换客服。
     */
    public function switchService(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group id')->required('group_id is require');
        $valitor->addColumn('app_user_id', 'app_user_id')->required('app_user_id is require');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'switchService',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }
        #普通用户无法切换客服
        if($userInfo['type']!=UserModel::USER_TYPE_SERVICE&&$userInfo['type']!=UserModel::USER_TYPE_ADMIN){
            $messageArr['code'] = ReponseCodeService::CODE_NOT_SERVICE;
            $messageArr['msg'] = 'faile';
            return  $this->formateReturn(  $messageArr);
        }

        $chatService = new ChatService();
        $ret = $chatService->switchService($param, $userInfo);
        if($ret ==true){
            $messageArr['code'] = ReponseCodeService::CODE_OK;
            $messageArr['msg'] = 'sucess';
            return  $this->formateReturn(  $messageArr);
        }
        $messageArr['code'] = $ret;
        $messageArr['msg'] = 'faile';
        return  $this->formateReturn(  $messageArr);
    }

    /**
     * 单聊历史
     * showdoc
     * @catalog Websocket
     * @title  获取单聊历史消息
     * @description 获取单聊历史消息
     * @method ws
     * @param extra.msg_id 否 string 这个聊天对象的最后一条消息id ，传了会从这个id往后拉取
     * @param extra.app_user_id 是 string 对方app_user_id，必须传
     * @param extra.count 是 string 拉取条数，默认20条
     * @param action  是 string   动作，chatHistory
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 拉取单聊的历史消息，用户群聊同步消息,返回消息格式同群聊消息
     */
    public function chatHistory(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('app_user_id', 'app_user_id')->required('app_user_id is require');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'chatHistory',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $msgService = new MsgService();
        $list =  $msgService->privateList($param, $userInfo);
        if(is_array($list)){
            $data['extra']['list'] =  $list;
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_OK,
                'data'=>json_encode($data),
                'action'=>'chatHistory',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
        }else{
            $messageArr = array(
                'code'=>$list,
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'chatHistory',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
        }
        return  $this->formateReturn($messageArr);
    }


    /**
     * 群聊历史
     * showdoc
     * @catalog Websocket
     * @title  获取群聊历史消息
     * @description 获取群聊历史消息
     * @method ws
     * @param extra.msg_id 否 string 群的消息id，传了会从这个id往后拉取
     * @param extra.group_id 是 string 群id，必须传
     * @param extra.count 是 string 拉取条数，默认20条
     * @param action  是 string   动作，groupChatHistory
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 拉取群的历史消息，用户群聊同步消息,返回消息格式同群聊消息
     */
    public function groupChatHistory(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group id')->required('group_id必须传');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'groupChatHistory',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $msgService = new MsgService();
        $list =  $msgService->groupList($param, $userInfo);
        if(is_array($list)){
            $data['extra']['list'] =  $list;
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_OK,
                'data'=>json_encode($data),
                'action'=>'groupChatHistory',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
        }else{
            $messageArr = array(
                'code'=>$list,
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'groupChatHistory',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
        }
        return  $this->formateReturn($messageArr);
    }


    /**
     * 邀请入群
     * showdoc
     * @catalog Websocket
     * @title  邀请入群
     * @description 邀请入群的接口
     * @method ws
     * @param extra.app_user_ids 是 string 群成员id，多个app_user_id用,分隔，如：1,2
     * @param extra.group_id 是 string 群id，必须传
     * @param action  是 string   动作，inviteJoinGroup
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  ['app_user_id']['ret'] int 邀请结果，true成功，false失败
     * @remark 前端只要管结果有没有成功，不管人员在本地是否添加，需要添加人员走服务端的用户入群变动系统通知
     */
    public function inviteJoinGroup(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $extra =  isset($param['extra'])?$param['extra']:array(1);
        $valitor = new Validate();
        $valitor->addColumn('app_user_ids', '用户id')->required('用户app_user_id必须传');
        $valitor->addColumn('group_id', 'group id')->required('group_id必须传');
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'inviteJoinGroup',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'user_id'=>$userInfo['id'],
        );
        $bool = $valitor->validate($extra);
        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            return  $this->formateReturn(  $messageArr);
        }

        $groupService = new GroupService();
        return  $this->formateReturn( $groupService->inviteMember($param,$userInfo));
    }


    /**
     * 删除黑名单
     * showdoc
     * @catalog Websocket
     * @title  删除黑名单
     * @description 删除黑名单
     * @method ws
     * @param action  是  string  动作，delBlacklist
     * @param extra.app_user_id 是 string 黑名单
     * @return
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 删除黑名单后，可以给对方发消息
     */
    public function delBlacklist(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $valitor = new Validate();
        $valitor->addColumn('app_user_id', 'app_user_id')->required('app_user_id is require');
        $param['extra'] = isset($param['extra'])?$param['extra']:array(1);
        $bool = $valitor->validate( $param['extra']);
        $data = array(
        );
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode($data,JSON_FORCE_OBJECT),
            'action'=>'addBlacklist',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'msg'=>ReponseCodeService::$phrases[ReponseCodeService::CODE_BAD_REQUEST]
        );

        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            $messageArr['code'] = ReponseCodeService::CODE_BAD_REQUEST;
            return  $this->formateReturn($messageArr);
        }
        $userService = new UserService();
        $ret = $userService->delBlacklist($param,$userInfo);
        $messageArr = array(
            'code'=>$ret,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'delBlacklist',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'msg'=>ReponseCodeService::$phrases[$ret]
        );
        return  $this->formateReturn( $messageArr);
    }


    /**
     * 加入黑名单
     * showdoc
     * @catalog Websocket
     * @title  加入黑名单
     * @description 加入黑名单
     * @method ws
     * @param action  是  string  动作，addBlacklist
     * @param extra.app_user_id 是 string 黑名单
     * @return
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 加入黑名单后，无法给对方发消息
     */
    public function addBlacklist(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $valitor = new Validate();
        $valitor->addColumn('app_user_id', 'app_user_id')->required('app_user_id is require');
        $param['extra'] = isset($param['extra'])?$param['extra']:array(1);
        $bool = $valitor->validate( $param['extra']);
        $data = array(
        );
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode($data,JSON_FORCE_OBJECT),
            'action'=>'addBlacklist',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'msg'=>ReponseCodeService::$phrases[ReponseCodeService::CODE_BAD_REQUEST]
        );

        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            $messageArr['code'] = ReponseCodeService::CODE_BAD_REQUEST;
            return  $this->formateReturn($messageArr);
        }
        $userService = new UserService();
        $ret = $userService->addBlacklist($param,$userInfo);
        $messageArr = array(
            'code'=>$ret,
            'data'=>json_encode(array(),JSON_FORCE_OBJECT),
            'action'=>'addBlacklist',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
            'msg'=>ReponseCodeService::$phrases[$ret]
        );
        return  $this->formateReturn( $messageArr);
    }




    /**
     * 移除群成员
     * showdoc
     * @catalog Websocket
     * @title  移除群成员
     * @description 移除群成员
     * @method ws
     * @param action  是  string  动作，removeMember
     * @param extra.group_id 是 string 群id
     * @param extra.app_user_ids 是 string 群成员的app_user_id，多个成员请用,拼接，例如：572402b0988e33369ad8ff0d3cd440a9（单个）或者572402b0988e33369ad8ff0d3cd440a9,asfsegesregreg（d多个）
     * @return
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @remark 注意，只有群主或者管理员才有权限踢人，踢人的服务端消息会走系统消息，详情见主动推送
     */
    public function removeMember(){
        $param = $this->caller()->getArgs();
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group_id')->required('group_id is require');
        $valitor->addColumn('app_user_ids', 'app_user_ids')->required('app_user_ids is require');
        $param['extra'] = isset($param['extra'])?$param['extra']:array(1);

        $bool = $valitor->validate( $param['extra']);
        $data = array(
        );
        $messageArr = array(
            'code'=>ReponseCodeService::CODE_BAD_REQUEST,
            'data'=>json_encode($data,JSON_FORCE_OBJECT),
            'action'=>'removeMember',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
        );

        if (!$bool) {
            $messageArr['msg'] = $valitor->getError()->__toString();
            $messageArr['code'] = ReponseCodeService::CODE_BAD_REQUEST;
            return  $this->formateReturn($messageArr);
        }
        $groupService = new GroupService();

        return  $this->formateReturn($groupService->removeMember($param));
    }




    /**
     * 获取客服群信息
     * showdoc
     * @catalog Websocket
     * @title  获取客服群信息
     * @description 获取客服群信息
     * @method ws
     * @param action  是  string  动作，getServiceGroup
     * @return {"code":200,"msg":"OK","data":"{\"extra\":{\"group_id\":74}}","action":"getServiceGroup","nonce":1594205104,"time":1594205104684,"user_id":29}
     * @return_param  code int 状态码(200:成功0:失败400:未登录)
     * @return_param  group_id string 客服群id
     * @remark  客服本质是个群，用户找客服聊天，获取群会自动拉取群号，用户拿着群号发消息即可.这个群号是固定的，不会改变，客户端可以永久存储。
     */
    public function getServiceGroup(){
        $param = $this->caller()->getArgs();
        $groupService = new GroupService();
        return  $this->formateReturn($groupService->getServiceGroup($param));
    }

    /**
     * showdoc
     * @catalog Websocket
     * @title  一对一聊天
     * @description 一对一聊天
     * @method websocket
     * @url /
     * @param content string  是 发送的内容
     * @param to_app_user_id string 是 接收消息的用户app_user_id
     * @param action  string  是 动作，固定是chat
     * @param  nonce string 消息随机字符串，由时间戳+6位随机字符串，用于消息校验
     * @param type string 消息类型：1文本2语音3图片4地理位置5红包6名片
     * @param  extra string 额外信息，有需要增加的字段，全部往这边塞
     * @return {"code":200,"msg":"用户id6在本机，已经推送了消息","content":''}
     * @return_param  code int 状态码(200:成功0:失败401:未登录)
     * @return_param  data string 返回结果
     * @return_param  msg string 返回提示语
     * @return_param  nonce string 消息随机字符串，由时间戳+6位随机字符串，用于消息校验
     * @return_param  msg_id string  消息id
     * @return_param  user_id 发送人id
     * @return_param nickname string 昵称
     * @return_param type string 消息类型：见统一说明
     * @remark  对方接受消息，会得到以下字段，内容content，发送人昵称nickname，发送人头像head，消息id ,action还是chat,消息发送时间time,发消息人的用户id 即user_id。文本类消息直接放content。
     */
    function chat(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $valitor = new Validate();
        $valitor->addColumn('content', '文本')->required('文本必须传');
        $valitor->addColumn('nonce', '随机字符串')->required('随机字符串必须传');
        $valitor->addColumn('to_app_user_id', '用户to_app_user_id')->required('用户to_app_user_id必须传');
        $valitor->addColumn('type', '类型')->required('类型必须传')->numeric('类型格式错误')->between(0,30);

        $bool = $valitor->validate($param);

        if(! $bool){
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_BAD_REQUEST,
                'msg'=>$valitor->getError()->__toString(),
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'chat',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
            return  $this->formateReturn($messageArr);
        }

        $userLogic = new UserLogic();
        $toUserInfo = $userLogic->findByAppUserIdByAppId($userInfo['app_id'],$param['to_app_user_id']);

        if(!is_object( $toUserInfo)){
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_BAD_REQUEST,
                'msg'=>ReponseCodeService::$phrases[ReponseCodeService::CODE_BAD_REQUEST],
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'chat',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
            return  $this->formateReturn(  $messageArr);
        }
        $param['to_user_id'] =   $toUserInfo['id'];
        $param['to_app_user_id'] = $toUserInfo['app_user_id'];
        $param['extra'] = isset($param['extra'])?$param['extra']:array();
        #提升并发，异步推送
            $msgService = new MsgService();
            $messageArr =   $msgService->priveteChat($param,$userInfo);
            return  $this->formateReturn(  $messageArr);

    }


    /**
     * 退出群
     * showdoc
     * @catalog Websocket
     * @title  退出群
     * @description 退出群的接口
     * @method ws
     * @param extra.group_id  是 string   群的id
     * @param action  是 string   动作，leaveGroup
     * @return {"code":200,"result":'',"msg":"创建成功"}
     * @return_param  code int 状态码(200:成功0:失败400)
     *@return_param  extra.group_id  string 群id
     * @remark  注意了，退群本人会收到消息，本群其他人后续也会收到系统消息，具体可以参考系统消息说明
     */
    public function leaveGroup(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $valitor = new Validate();
        $valitor->addColumn('group_id', 'group_id')->required('群id必须传');
        $bool = $valitor->validate($param['extra']);
        if (!$bool) {
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_BAD_REQUEST,
                'msg'=>$valitor->getError()->__toString(),
                'data'=>json_encode(array(),JSON_FORCE_OBJECT),
                'action'=>'leaveGroup',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                'user_id'=>$userInfo['id'],
            );
            return  $this->formateReturn(  $messageArr);
        }

        $groupService = new GroupService();
        return  $this->formateReturn($groupService->leaveGroup($param));
    }

    /**
     * showdoc
     * @catalog Websocket
     * @title 连接socket
     * @description 连接socket
     * @method websocket
     * @url /
     * @param access_key string    是 app的access_key
     * @remark  1.建立连接，必须在header传access_key。2.如果连接成功 Status Code会切换为101。3.每次发消息，请带时间戳加5位随机数，用于校验消息的发送响应。后续文档会做具体说明。4.我们约定，全部数据上传之前，都把json字符串，aes加密。服务器下发也是如此。
     */
    protected function cnnsocket(){

    }


    /**
     * showdoc
     * @catalog Websocket
     * @title  获取七牛授权
     * @description 获取七牛授权
     * @method websocket
     * @url /
     * @param content string  是 发送的内容
     * @param action  string  是 动作，固定是qiniuAuth
     * @param  nonce string 消息随机字符串，由时间戳加6位随机字符串，用于消息校验
     * @return {"code":200,"msg":"在线","content":''}
     * @return_param  code int 状态码(200:成功0:失败401:未登录)
     * @return_param  data string 返回结果
     * @return_param  msg string 返回提示语
     * @return_param  nonce string 消息随机字符串
     * @return_param  extra.token string  授权token
     * @return_param  extra.folder string  文件存储目录
     * @remark
     */
    public function qiniuAuth(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $valitor = new Validate();
        $valitor->addColumn('nonce', '随机字符串')->required('随机字符串必须传');
        $messageArr = array(
            'action'=>'qiniuAuth',
            'nonce'=>$param['nonce'],
            'time'=>HelpService::msectime(),
        );
        $bool = $valitor->validate($param);

        if (!$bool) {
            $messageArr['msg'] = 'faile';
            $messageArr['data'] = array();
            $messageArr['code'] = ReponseCodeService::CODE_BAD_REQUEST;
            return  $this->formateReturn($messageArr);
        }

        #$callbackBody = "hash=$(etag)&uid=$(x:uid)&name=$(fname)&size=$(fsize)&bucket=$(bucket)&mime=$(mimeType)";
        $callbackBody = "hash=$(etag)&user_id={$userInfo['id']}&app_id={$userInfo['app_id']}&name=$(fname)&size=$(fsize)&bucket=$(bucket)&mime=$(mimeType)";
        $policy = [
            "callbackUrl" =>  'http://'.Config::getInstance()->getConf('APP.CURRENT.host')."/Qiniucallback",
            "callbackBody" => $callbackBody,
            "fsizeLimit" => 150 * 1024 * 1024,
            //  "mimeLimit" => "audio/midi;audio/acc;audio/mpeg;audio/ogg;image/jpeg;image/png;image/jpg;image/gif;application/octet-stream;application/vnd.ms-excel;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/msword;video/mp4;video/avi;video/x-flv;video/mpeg;video/x-ms-wmv;video/quicktime;application/x-7z-compressed;application/zip;text/csv;text/plain;"
        ];

        $auth = new \EasySwoole\Oss\QiNiu\Auth(Config::getInstance()->getConf('QINIU.QINIU_ACCESS_KEY'),Config::getInstance()->getConf('QINIU.QINIU_SECRET_KEY'));
        $token = $auth->uploadToken('wangtuizhijia', null, 3600, $policy);
        $data['extra']['token']=$token;
        $data['extra']['folder'] = $userInfo['app_id'] ;
        $messageArr['msg'] = 'sucess';
        $messageArr['data'] =  $data;
        $messageArr['code'] = ReponseCodeService::CODE_OK;
        return  $this->formateReturn($messageArr);
    }

    /**
     * showdoc
     * @catalog Websocket
     * @title  心跳
     * @description 心跳
     * @method websocket
     * @url /
     * @param content string  是 发送的内容
     * @param action  string  是 动作，固定是ping
     * @param  nonce string 消息随机字符串，由时间戳加6位随机字符串，用于消息校验
     * @return {"code":200,"msg":"在线","content":''}
     * @return_param  code int 状态码(200:成功0:失败401:未登录)
     * @return_param  data string 返回结果
     * @return_param  msg string 返回提示语
     * @return_param  nonce string 消息随机字符串，由时间戳+6位随机字符串，用于消息校验
     * @return_param  time int  毫秒级时间戳
      * @remark
     */
    function ping(){
        $data = array(
            'content'=>'pong',
      );
        $reponse = array(
            'code'=>ReponseCodeService::CODE_OK,
            'msg'=>"连接正常",
            'data'=>json_encode($data),
            'action'=>'ping',
            'time'=>HelpService::msectime(),
        );
        return  $this->formateReturn($reponse);
    }



    /**
     * showdoc
     * @catalog Websocket
     * @title  群聊
     * @description 群聊
     * @method websocket
     * @url /
     * @param content string  是 发送的内容
     * @param group_id int  是 群id
     * @param type string 消息类型：见统一说明
     * @param action  string  是 动作，固定是groupChat
     * @param  nonce string 消息随机字符串，由时间戳+6位随机字符串，用于消息校验
     * @param  extra string 额外信息，有需要增加的字段，全部往这边塞
     * @return {"code":200,"msg":"已经推送了消息","content":''}
     * @return_param  code int 状态码(200:成功0:失败401:未登录)
     * @return_param  data string 返回结果
     * @return_param  msg string 返回提示语
     * @return_param  nonce string 消息随机字符串，由时间戳加6位随机字符串，用于消息校验
     * @return_param  msg_id string  消息id
     * @return_param type string 消息类型
     * @remark
     */
    function groupChat(){
        $param = $this->caller()->getArgs();
        $userInfo =  $this->caller()->getArgs()['userInfo'];
        $valitor = new Validate();
        $valitor->addColumn('content', '文本')->required('文本必须传');
        $valitor->addColumn('nonce', '随机字符串')->required('随机字符串必须传');
        $valitor->addColumn('group_id', '群id')->required('群id必须传')->numeric('群id是数字');
        $valitor->addColumn('type', '类型')->required('类型必须传')->numeric('类型格式错误')->between(0,30);
        $bool = $valitor->validate($param);

        $data = array(
            'group_id' =>$param['group_id'],
            'app_user_id'=>$userInfo['app_user_id'],
            'content'=>$param['content'],
            'nickname'=>$userInfo['nickname'],
            'head'=>$userInfo['head'],
            'type'=>$param['type'],
            'extra'=>isset($param['extra'])?$param['extra']:array(),
        );

        if (!$bool) {
            $messageArr = array(
                'code'=>ReponseCodeService::CODE_BAD_REQUEST,
                'msg'=>$valitor->getError()->__toString(),
                'data'=>json_encode($data,JSON_FORCE_OBJECT),
                'action'=>'groupChat',
                'nonce'=>$param['nonce'],
                'time'=>HelpService::msectime(),
                 'group_id' =>$param['group_id'],
                'app_user_id'=>$userInfo['app_user_id'],
            );
            return  $this->formateReturn(  $messageArr);
     }

            $msgService = new MsgService();
            $msgService->groupChat($param,$userInfo);
    }

}